<template>
  <div class="login-box">
    <!-- 使用定位，设置层叠效果 -->
    <canvas id="canvas" ref="canvas"></canvas>
    <LoginPanel class="login-container"></LoginPanel>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import LoginPanel from './components/LoginPanel.vue'
let canvas = ref(null)

/**
 * 初始化canvas粒子特效
 */
const initCanvasBg = () => {
  // 设置canvas充满屏幕
  canvas.value.width = window.innerWidth
  canvas.value.height = window.innerHeight
  // 获取canvas上下文,采用2d二维渲染上下文
  const ctx = canvas.value.getContext('2d')
  class Dot {
    x: number
    y: number
    dirX: number
    dirY: number
    color: string
    // 设置点位
    constructor(x: number, y: number) {
      this.x = x
      this.y = y
      this.dirX = Math.random() * 10 - 5 //运动速度均设置为随机数
      this.dirY = Math.random() * 10 - 5
      this.color = `hsl(${Math.random() * 360}, 50%, 50%)` //color中的hsl设置颜色：三个参数各位色相、饱和度、亮度
    }
    // 绘制方法
    draw() {
      // 准备创建新的路径，如果不清空上一个canvas会话的路径，就会沿用上一个的位置，所以每次都建议清空重新设置路径信息
      ctx.beginPath()
      ctx.fillStyle = this.color //设置颜色
      // 初始半径可以设大点查看效果，最终样式设小即可
      ctx.arc(this.x, this.y, 3, 0, Math.PI * 2) //画圆弧，各参数意思为：坐标x，y，半径，起始弧度，结束弧度
      ctx.fill() //开始填充
      ctx.closePath() //将所有的线连接起来
    }
    // 更新位置，并改变速度
    update() {
      // 碰到边界就反方向运动
      if (this.y > canvas.value.height || this.y <= 0) {
        this.dirY = -this.dirY
      }
      if (this.x > canvas.value.width || this.x <= 0) {
        this.dirX = -this.dirX
      }
      this.x += this.dirX //初始X往右
      this.y += this.dirY //初始Y往下
      this.color = `hsl(${Math.random() * 360}, 50%, 50%)`
      this.draw() //可以理解为将多个不同位置的圆快速连成一条线
    }
  }

  let dots = new Dot(400, 400) //初始化位置

  // 绘制多个图像
  const arr = []

  function init() {
    // 初始测试可以将数量设置小点查看效果，最终效果设置多
    for (let i = 0; i < 80; i++) {
      // 随机创建n个位置不同图像
      let dots = new Dot(Math.random() * canvas.value.width, Math.random() * canvas.value.height)
      arr.push(dots)
    }
  }

  init()

  // // 创建一个函数不断执行绘画
  function animation() {
    // 每次绘画前都清除上一个运动轨迹，即圆线条，这样子屏幕不会显示线，只会显示圆
    ctx.clearRect(0, 0, canvas.value.width, canvas.value.height)
    // 告诉浏览器——你希望执行一个动画，传入回调函数。准备更新在屏动画时你应该调用此方法
    requestAnimationFrame(animation)
    // 20个元素每个都进行绘画更新,外层循环画圆
    arr.forEach((item, index) => {
      // 内存循环将圆连接起来
      arr.forEach((items, indexs) => {
        // 同一个圆的情况下画线
        if (index === indexs) return
        // 设置画线条件
        if (Math.abs(item.x - items.x) < 120 && Math.abs(item.y - items.y) < 120) {
          item.color = `hsl(${Math.random() * 360}, 50%, 50%)`
          setLine(item.x, item.y, items.x, items.y, item.color)
        }
      })
      item.update()
    })
  }

  animation()

  // 设置圆之间连线
  function setLine(x: number, y: number, x1: number, y1: number, color: string) {
    ctx.beginPath()
    ctx.strokeStyle = color //设置画笔颜色
    ctx.moveTo(x, y) //起始位置
    ctx.lineTo(x1, y1) //结束位置
    ctx.stroke() //根据画笔颜色连接两个点形成一个线
    ctx.closePath()
  }
}

onMounted(() => {
  initCanvasBg()
  // 可以进行节流防抖
  document.addEventListener('resize', initCanvasBg)
})
onUnmounted(() => {
  document.removeEventListener('resize', initCanvasBg)
})
</script>

<style lang="scss" scoped>
.login-box {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
  #canvas {
    position: absolute;
    top: 0;
    left: 0;
    background-color: #000;
  }
  .login-container {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 99;
    width: 400px;
  }
}
</style>
